iT邦幫忙

2024 iThome 鐵人賽

0
Kubernetes

一起來看 Kubernetes 官方文件吧!系列 第 13

Day13 - 一起來看 Kubernetes 官方文件吧!- Pod lifecycle (下)

  • 分享至 

  • xImage
  •  

前言

繼續來看前一天的文件

今日目標

  • 了解 Pod readiness
  • 了解 Probe 有哪幾種

Pod readiness

此功能是擴增 pods status 的欄位,可以自訂欄位名稱,且只要當此欄位尚未 Ready 時,pods 的 Ready 欄位就會是 false
此欄位的判斷方式就是透過 API 調整欄位啟用,預設是關閉的。
也就是說,判斷的邏輯規則是透過外部的 APP 來處理的。

pod readiness 的功能從 k8s 1.14 之後就進入 stable 的狀態,不過筆者是沒特別看過太多使用情境,翻找他人的範例都是透過自己寫程式的方式來讓此 status 改為 true
文件內有特別提到,不能使用 kubectl patch 的方式讓 pod readiness 通過,必須要透過 PATCH 的 action 打 apiserver 才行

嘗試看看此功能,在一個 nginx 的 deployment 加上:

...
spec:
  template:
    spec:
      readinessGates:
      - conditionType: "www.example.com/gate-1"
...

然後會發現,所有 pods 啟動時都是 1/1 Ready 的狀態,後面則多出了 readiness gate 的欄位
查看 endpoint 沒有轉送流量到 pods

❯ k get po -o wide  
NAME                         READY   STATUS    RESTARTS   AGE   IP           NODE          NOMINATED NODE   READINESS GATES
nginx-test-ff55c4d84-547jt   1/1     Running   0          9s    10.0.2.5     k8s-master3   <none>           0/1
nginx-test-ff55c4d84-rrrvj   1/1     Running   0          9s    10.0.0.213   k8s-master1   <none>           0/1
nginx-test-ff55c4d84-sr8x8   1/1     Running   0          9s    10.0.2.83    k8s-master3   <none>           0/1
nginx-test-ff55c4d84-thwlb   1/1     Running   0          9s    10.0.2.48    k8s-master3   <none>           0/1
nginx-test-ff55c4d84-xdbm2   1/1     Running   0          9s    10.0.0.17    k8s-master1   <none>           0/1
❯ k get ep
NAME         ENDPOINTS                               AGE
kubernetes   192.168.75.11:6443,192.168.75.13:6443   3d5h
nginx-test                                           12m

參考他人文章,使用 kubectl proxy 開啟一個 port,後續指定某 pods name 透過 curl 發送請求修改內容:

❯ kubectl proxy --port 12345 &
❯ curl -k \
     -H "Content-Type: application/json-patch+json" \
     -X PATCH http://localhost:12345/api/v1/namespaces/default/pods/nginx-test-ff55c4d84-547jt/status \
     --data '[{ "op": "replace", "path": "/status/conditions/0",
                "value": { "type": "www.example.com/gate-1", "status": "True" }}]'
❯ k get po -o wide
NAME                         READY   STATUS    RESTARTS   AGE     IP           NODE          NOMINATED NODE   READINESS GATES
nginx-test-ff55c4d84-547jt   1/1     Running   0          3m34s   10.0.2.5     k8s-master3   <none>           1/1
nginx-test-ff55c4d84-rrrvj   1/1     Running   0          3m34s   10.0.0.213   k8s-master1   <none>           0/1
nginx-test-ff55c4d84-sr8x8   1/1     Running   0          3m34s   10.0.2.83    k8s-master3   <none>           0/1
nginx-test-ff55c4d84-thwlb   1/1     Running   0          3m34s   10.0.2.48    k8s-master3   <none>           0/1
nginx-test-ff55c4d84-xdbm2   1/1     Running   0          3m34s   10.0.0.17    k8s-master1   <none>           0/1
❯ k get ep
NAME         ENDPOINTS                               AGE
kubernetes   192.168.75.11:6443,192.168.75.13:6443   3d5h
nginx-test   10.0.2.5:80                             30m

讓其中一個 pods 的 readiness gate 通過後,就會發現 service 才會相對應轉送流量過去

這個功能應該是要開發 operator 的人員會比較可能用到,透過外部的機制來把控 APP 是否可用。
不過筆者對這個功能真的不熟,只是試用看看,或許有更方便使用的應用場景 ~

Pod network readiness

在以前的版本中,此狀態又被稱作 PodHasNetwork

主要是描述 PodReadyToStartContainersCondition 的這個狀態是如何判斷的,當 kubelet 正常啟用 pods 後,此欄位即為 True,為 False 時通常是以下情況:

  • pods 剛被建立:kubelet 還沒透過 CRI 建立 pod sandbox (pause container)
  • pods 正在被刪除,因為以下情況:
    • node 重開機,但是 pod 沒有被驅除
    • CRI 使用 VM 做到隔離,pod sandbox 需要建立新的已重新建立 container network

在此狀態設定為 True 之後,kubelet 才會開始去拉 container image (還蠻有趣的先後順序)

當 pods 有設定 initcontainer 時,會先建立 sandbox container,再執行 initcontainer,當 initcontainer 建立完成後。才會把 Initialized 設為 True
若無 initcontainer,則會在 sandbox container 建立之前,就將 Initialized 設為 True (算是無傷大雅的順序?)

Container probes

若要讓部署在 k8s 上的 App 有更完善的健康管理機制,可以考慮加入 probe(探針)
檢查的方式有以下幾種:

  • exec:指定執行 linux 上的指令,當 exit status code 為 0 時就代表通過。
    算是最通用的方式,不管想要客製化檢查檔案,檢查 port 等等都可以用
  • grpc:發送 grpc health checks 到某個 service,只要回傳 SERVING 就代表通過
  • httpGet:發送 Http Get 到某個 endpoint 確認是否有通,當回傳的代碼介於 200 ~ 399 之間代表通過
  • tcpSocket:檢查某個 TCP port 是否有通,只需要 port 有連通就代表通過

官方建議在使用 exec probe 時要小心,當 pods 數量很多且  initialDelaySecondsperiodSeconds 設定較小時,很可能會增加節點的 cpu 使用率。
在這樣的情況下,建議採取其他的偵測機制避免過多的資源浪費。

probes 會有三種的執行結果:

  • Success:通過診斷
  • Failure:未通過診斷
  • Unknown:診斷失敗,且 kubelet 會持續追查問題 (使用者不需要執行動作)

這幾個狀態應該都蠻容易理解的

Types of probe

可以設定三種不同類型的 probe 到 container 上:

  • livenessProbe:確保 containers 活著,當 livenessProbe 診斷失敗,kubelet 會刪掉此 container,並以 restartpolicy 決定是否重啟。
  • readinessProbe: 確保 containers 已經可以接受流量。當 readinessProbe 未通過診斷時,endpoint controller 會在 endpoint 移除該 pods IP 避免 service 轉送流量至未準備好的 container。
  • startupProbe:當 containers 剛開啟時套用,當 startupProbe 還在診斷階段時,其他的 Probe 都還不會運作。當 startupProbe 診斷失敗,kubelet 會刪掉此 container,並以 restartpolicy 決定是否重啟。

至於總共要判斷幾次,多久內會失敗則是透過以下幾個參數:

  • initialDelaySeconds :經過多少秒數後,才會開始偵測
  • failureThreshold:總共失敗幾次才會算失敗 (預設為 3)
  • periodSeconds:多久偵測一次 (預設為 10s)
  • successThreshold:僅能設定於 readiness probe,probe 成功幾次才算 ready (liveness 與 startup 必須為 1)
  • timeoutSeconds:probe 執行超過多久就算是 timeout (預設為 1s)

也就是說總共的失敗時間計算方式為:

initialDelaySeconds + failureThreshold × periodSeconds

https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ 的文件中有詳細介紹 Probe 的用法,可以看其中以下的一個範例:

apiVersion: v1
kind: Pod
metadata:
  name: goproxy
  labels:
    app: goproxy
spec:
  containers:
  - name: goproxy
    image: registry.k8s.io/goproxy:0.1
    ports:
    - containerPort: 8080
    readinessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 15
      periodSeconds: 10
    livenessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 15
      periodSeconds: 10

此範例設定了 readinessProbelivenessProbe,且都是偵測此 container 的 TCP 8080 port 是否可用,當 8080 port 不可用且持續一段時間後,livenessProbe 就會觸發 kubelet 將 container 移除。

When should you use a liveness probe?

liveness probe 觸發時,kubelet 會嘗試將 container 移除,通常是為了避免 container 有 “活著但無法提供服務” 的現象發生,才會需要配置 liveness probe,否則若是 APP 本身的 error handling 都有正確配置的話,或許可以考慮不需要加入 liveness probe。

liveness probe 觸發時,exit status 會回傳 error,也就是說當 container 設定 restartPolicyOnFailure 時也會重啟。

When should you use a readiness probe?

readiness probe 是用來確保 container 已經準備好接受流量,使用情境大致有:

  1. APP 本身有設計 /ready 等 API endpoint:readiness probe 可以設計去戳此 endpoint,以結合 APP 本身的管理機制
  2. APP 與某些其他服務有 “高度的耦合” (e.g. 後端必須要有資料庫才能運作):可以將 readiness probe 設定到外部的服務,可確保後端 APP 不會沒辦法與資料庫互動。

當 pods 在接收到 delete 的指令時,會預設將內部所有的 container 設為 not ready 的狀態,也就是在關閉期間會停止轉送流量過去。
因此在這樣的情境下就不會是 readiness probe 考量的情境。

When should you use a startup probe?

當 container 被設計會需要較長的啟動時間時,設定 startup probe 可以確保 container 正常啟動後,才開始後續的動作 (e.g. readiness probe 才開始偵測)

不過在設計 startup probe 時要配置好足夠的時間讓 APP 啟動,否則可能 APP 會一直卡在被 startup probe 持續刪掉的無限循環中。

Termination of Pods

若要讓 pods 內的 APP 正常退出,就不能僅僅只是送 KILL 訊號導致 process 忽然中斷,這可能會造成資料的損毀或請求傳送不完全等等

常見的 Unix 離開訊號有兩種:

  • SIGKILL (9):最暴力的方式,直接強制 process 關閉,此訊號也無法被捕捉
  • SIGTERM (15):比較溫和的關閉方式,APP 可以捕捉此訊號進行 “優雅的” 關閉

kubelet 預設會嘗試 graceful shutdown,大致流程為:

  1. user 使用 kubectl delete 指定 pods(預設會帶入 grace period 30s)
  2. API server 更新此 pods 狀態為 “dead”。如果使用 kubectl describe 去看會發現 pods 會顯示 “Terminating” 的狀態
    當 kubelet 發現此 pods 為 Terminating 的狀態時,就會開啟 local pods shutdown 的流程:
    1. 若是 pods 內的 container 有配置 preStop hook 且 terminationGracePeriodSeconds 不為 0 (預設為 30s),則 kubelet 會在 container 內執行此 hook
      當 preStop 的時間到了, kubelet 會請求延長 2 秒的時間處理
    2. kubelet 會觸發 container runtime 發送 TERM 訊號給每個 container 中 PID 為 1 的 process
      基本上是同時,但若是有設定 sidecar container 的話會用特別的順序,或者若 pods 內 container 也有必須指定的關閉順序,就要考慮配置好 preStop hook 來同步。
  3. 在此期間,control plane 會開始評估是否刪除 pods 上對應的 EndpointSilce (或 Endpoints) 資源,若 pods 是由其他控制器管理 (e.g. ReplicaSets),也會通知 pods 已經不可用
    關閉中的 pods 不應該再繼續接收流量,某些 APP 也應該要完成一些作業 (e.g. close connection)
    正在 terminating 的 pods 不會立刻被
  4. kubelet 會確保 pods 已經關閉
    1. 當超過 grace period 後,若 pods 內還有 container 在運行,container runtime 會發送 SIGKILL 強制關閉 container,kubelet 也會把隱藏的 pause container 關閉
    2. kubelet 把 pods 轉為 terminal phase (Failed or Succeed)
    3. kubelet 會把 grace period 設為 0,也就是強制關閉
    4. API server 會把 pods 的資訊刪除,也就無法在 client 上看到

Forced Pod termination

要強制把 pods 刪除,可以使用:

kubectl delete pods <pod-name> --force --grace-period=0

這樣做會立刻從 apiserver 上面將該 pods 刪除,不會等 kubelet 確認 pods 刪除,因此即使 kubectl get 看不到該 pods,此 pods 還是有可能持續在該 node 上運行

另外要注意強制刪除由 statefulset 有特別的流程要注意 (主要是 statefulset 的 pods 名稱是固定的,因此要特別小心)

StatefulSet considerations

https://kubernetes.io/docs/tasks/run-application/force-delete-stateful-set-pod/

因為當 node 進入 “unreachable” 的狀態時,並不會自動關閉 pods,當 user 手動刪除時,pods 的狀態會停在 'Terminating' or 'Unknown’,當 pods 進入這兩種情況時,有三種行為可以讓 pods 刪除:

  1. 該 Node 物件被刪除
  2. 該 Node 上的 kubelet 回應了(也就是 node 恢復正常了)
  3. user force 刪除 pods

官方是建議用 1,2 兩種,因為 3 暴力刪除很可能導致 statefulset 異常

但若是 force 刪除之後,還是有異常的話,就必須再加上:

# 原本的指令
# kubectl delete pods <pod> --grace-period=0
# 再加上
kubectl patch pod <pod> -p '{"metadata":{"finalizers":null}}'

不過這樣刪除似乎 “強烈不建議”,還是建議把節點救回來 or 直接把節點刪除

在 k8s node 失聯後,雖然可以看到 node 的狀態會轉成 NotReady,但是該節點上的 pods 卻還是會保持著 running 狀態,
雖然在 statefulset force delete 的這篇文章有寫到說:當 node unreachable 超過一定時間後,該 node 上的 pods 狀態都會 Ready → Unknown,但筆者目前的測試 cluster 並不會這樣處理,將 node 關機一天後,pods 依然是 Running 狀態,只有當 Node 重新開機後,會發現 pods 的 restart 次數 +1
等到後續看到 controller-manager 的機制再來深入研究研究…

Pod shutdown and sidecar containers

當 pods 有 sidecar containers 存在時,刪除時 sidecar containers 會是最後才接受到 SIGTERM 訊號的 container
不過要是整個 pods 的刪除時間過長,超過了 graceperiod 的話,kubelet 還是會同時對所有的 container 發送 SIGKILL 強制關閉訊號
同理當有設定 preStop hook 且導致時間超過的話,也是會整個 pods 同時刪除

Garbage collection of Pods

在 control plane 會有一個 Pod garbage collector (PodGC),會將以終止的 pods 清除

PodGC 會清除符合以下條件的 pods:

  1. 孤兒 pods:綁定的節點已經消失的 pods
  2. 處於 unscheduled terminating 狀態的 pods
  3. 處於 terminating 的 pods,且該 non-ready node 被打上了 node.kubernetes.io/out-of-service的 taint

若要更暸解 PodGC 的運作機制的話,就要再看看 pod 的中斷情況了。

結論

pods 可以說是 k8s 最主要的靈魂,負責執行 APP 提供服務
把 pods 的大大小小事情打理好,就可以提昇 pods 的可用度囉
不過也因為牽扯的東西較多,還需要再補助看看其他文件,才能整理出一個比較完整的內容…

參考

https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/

https://kubernetes.io/docs/tasks/run-application/force-delete-stateful-set-pod/

https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/

https://kubernetes.io/docs/concepts/workloads/pods/disruptions/#pod-disruption-conditions

https://martinheinz.dev/blog/63


上一篇
Day12 - 一起來看 Kubernetes 官方文件吧!- Pod lifecycle (上)
下一篇
Day14 - 一起來看 Kubernetes 官方文件吧!- Container Lifecycle Hooks
系列文
一起來看 Kubernetes 官方文件吧!19
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言